<template>
  <div class="home">
    <el-alert
      title="找不到根节点？别担心，刷新一下好了。还有，别堆太多层。什么？不知道下面的按钮什么意思？点几下就知道了，反正，电脑又不会冒烟！试着点击下面那排蓝色的数字，有惊喜哦 !-_-"
      type="success"
      closable
      @close="alertClose"
      v-if="alertStation"
      v-once
    ></el-alert>
    <h2>二叉树模拟器</h2>
    <!-- 输入二叉树的值 -->
    <el-row>
      <el-col :span="2">
        <el-button type="danger" @click="defaultTreeData">默认值</el-button>
      </el-col>
      <el-col :span="2">
        <el-button type="danger" @click="avlTree">手动平衡</el-button>
      </el-col>
      <el-col :span="12" :offset="2">
        <el-input
          placeholder="请输入0~99的整数且不重复的值用于树的节点，节点值之间用空格隔开，如：1 2 3"
          v-model="treeData"
          clearable
          @keyup.enter.native="makeTree"
          autofocus
          @keydown.ctrl.90.prevent.native="backupInputHistory"
          @keydown.ctrl.89.prevent.native="forwardInputHistory"
        >
          <el-button
            slot="append"
            icon="el-icon-search"
            @click="makeTree"
          ></el-button> </el-input
      ></el-col>
      <!-- 4个逆转未来的按钮 -->
      <el-col :span="1">
        <el-button
          type="danger"
          icon="el-icon-toilet-paper"
          circle
          @click="getColor"
        ></el-button>
      </el-col>
      <el-col :span="1"
        ><el-button
          type="danger"
          icon="el-icon-magic-stick"
          circle
          @click="treeNodeSmaller"
        ></el-button
      ></el-col>
      <el-col :span="1"
        ><el-button
          type="danger"
          icon="el-icon-sunny"
          circle
          @click="treeNodeBigger"
        ></el-button
      ></el-col>
      <el-col
        :span="1"
        style="height: 40px; display: flex; align-items: center;"
      >
        <!-- 树能不能动就看我了 -->
        <el-switch v-model="mapRoam" active-color="#ff4949"> </el-switch>
      </el-col>
    </el-row>
    <br />
    <!-- 显示输入的值 -->
    <div>
      <el-tag type="danger" effect="dark">
        您输入的值依次是：
      </el-tag>
      <el-tag
        v-for="item in showTreeData"
        :key="item"
        @click.stop="removeNode(item)"
        class="show-input"
      >
        <span title="删除">
          {{ item }}
        </span>
      </el-tag>
    </div>

    <br />
    <el-row>
      <el-col :span="24">
        <div id="container"></div>
      </el-col>
    </el-row>
    <br />
    <!-- 显示遍历的值 -->
    <div>
      <el-tag type="danger" effect="dark"> 中序遍历的值： </el-tag>
      <el-tag v-for="(item, index) in inOrderData" :key="index">{{
        item
      }}</el-tag>
    </div>
    <br />
    <div>
      <el-tag type="danger" effect="dark"> 先序遍历的值： </el-tag>
      <el-tag v-for="(item, index) in preOrderData" :key="index">{{
        item
      }}</el-tag>
    </div>
    <br />
    <div>
      <el-tag type="danger" effect="dark"> 后序遍历的值： </el-tag>
      <el-tag v-for="(item, index) in postOrderData" :key="index">{{
        item
      }}</el-tag>
    </div>
  </div>
</template>

<script>
// @ is an alias to /src AVLTree
import BinarySearchTree from "@/assets/tree/tree.js";
import History from "@/assets/tree/history.js";
import AVLTree from "@/assets/tree/avlTree.js";
import * as echarts from "echarts";
export default {
  name: "Home",
  data() {
    return {
      treeData: "", // 输入二叉树的值
      showTreeData: [], //展示输入的值
      mapData: [], // echarts的data
      mapLinks: [], // 节点间的连接
      mapColor: "#FF0033",
      mapRoam: true, // 放大缩小属性
      mapSymbolSize: 25, // 节点的大小
      mapText: "", // 图表名字
      inOrderData: [], // 中序遍历的值
      preOrderData: [], // 先序遍历的值
      postOrderData: [], // 后序遍历的值
      treeTitle: "", // 记录树的类型，普通树或自平衡
      inputHistory: undefined, // 记录输入的历史，用于前进后退
      alertStation: Boolean, // 提示信息栏状态
    };
  },
  components: {},
  created() {
    this.getInputData();
    this.inputHistory = new History();
  },
  mounted() {},
  methods: {
    // 提示栏关闭
    alertClose() {
      this.alertStation = false;
    },
    // 前进与后退
    // Ctrl+Y
    forwardInputHistory() {
      this.treeData = [];
      this.treeData = this.inputHistory.forword();
    },
    // Ctrl+Z
    backupInputHistory() {
      this.treeData = [];
      this.treeData = this.inputHistory.back();
    },
    // 点击展示您输入的值依次是后面那排数字，删除该节点，重新渲染树
    removeNode(item) {
      this.showTreeData.splice(this.showTreeData.indexOf(item), 1);
      this.treeData = this.showTreeData.join(" ");
      if (this.treeTitle === "nomalTree") {
        this.makeTree();
      } else if (this.treeTitle === "avlTree") {
        this.avlTree();
      }
    },
    // 三个遍历
    traverse(tree) {
      this.inOrderData = [];
      this.preOrderData = [];
      this.postOrderData = [];
      tree.inOrderTraverse((item) => {
        this.inOrderData.push(item);
      });
      tree.preOrderTraverse((item) => {
        this.preOrderData.push(item);
      });
      tree.postOrderTraverse((item) => {
        this.postOrderData.push(item);
      });
    },
    // 主要是为了二叉树太野不知道跑哪里去了，刷新后可以默认已经输入的值
    getInputData() {
      let sessionStorageTreeData = window.sessionStorage.getItem("treeData");
      let alertStation = window.sessionStorage.getItem("alertStation");
      this.alertStation = alertStation === "false" ? false : true;
      if (sessionStorageTreeData === null) {
        this.treeData = "11 7 5 3 6 9 8 10 15 13 12 14 20 18 25";
      } else {
        this.treeData = sessionStorageTreeData;
      }
    },
    // AVL-Tree
    avlTree() {
      // 首先生成AVL平衡树，再先序遍历取值后插入二叉搜索树
      // 这样的性能极低，但我懒得动valTree.js了，反正最多100个节点
      this.mapText = "极其舒适的自平衡树";
      this.showTreeData = this.checkTreeData(this.treeData);
      let tempTree = new AVLTree();
      this.showTreeData.forEach((item) => {
        tempTree.insert(item);
      });
      let showTreeData = [];
      tempTree.preOrderTraverse((item) => {
        showTreeData.push(item.key);
      });
      this.buildTree(showTreeData);
      this.treeTitle = "avlTree";
      this.inputHistory.insert(this.showTreeData.join(" "));
    },
    // 树节点变小
    treeNodeSmaller() {
      if (this.mapSymbolSize > 5) {
        this.mapSymbolSize -= 5;
      } else if (this.mapSymbolSize > 0) {
        this.mapSymbolSize -= 1;
      }
      this.tree();
    },
    // 树节点变大
    treeNodeBigger() {
      this.mapSymbolSize += 5;
      this.tree();
    },
    // echarts就不会瞎几把动了
    dontMove() {
      this.mapRoam = !this.mapRoam;
      this.tree();
    },
    // 变换树节点的颜色
    getColor() {
      let str = "#";
      //一个十六进制的值的数组
      let arr = [
        "0",
        "1",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "a",
        "b",
        "c",
        "d",
        "e",
        "f",
      ];
      for (let i = 0; i < 6; i++) {
        //产生的每个随机数都是一个索引,根据索引找到数组中对应的值,拼接到一起
        let num = parseInt(Math.random() * 16);
        str += arr[num];
      }
      this.mapColor = str;
      this.tree();
    },
    defaultTreeData() {
      this.treeData = "11 7 5 3 6 9 8 10 15 13 12 14 20 18 25";
    },
    // 只是为了复用而拆的
    buildTree(showTreeData) {
      let tempTree = new BinarySearchTree();
      showTreeData.forEach((item) => {
        tempTree.insert(item, 0);
      });
      // 最深的节点
      let deeperLevel = this.searchDeeperLevel(tempTree);
      // 接下来给echarts的data和links赋值
      this.mapData = [];
      this.mapLinks = [];
      let tree = new BinarySearchTree();
      showTreeData.forEach((item) => {
        tree.insert(item, deeperLevel);
      });
      tree.preOrderTraverse((item) => {
        let tempData = {};
        let tempLeftLink = {};
        let tempRightLink = {};
        tempData.name = item.key;
        tempData.x = item.x;
        tempData.y = item.y;
        tempData.level = item.level;
        tempLeftLink.source = item.toString();
        tempRightLink.source = item.toString();
        tempLeftLink.target = item.leftTarget;
        tempRightLink.target = item.rightRarget;
        this.mapData.push(tempData);
        this.mapLinks.push(tempLeftLink);
        this.mapLinks.push(tempRightLink);
      });
      // 赋值完成，生成树
      this.tree();
      this.traverse(tree);
    },
    makeTree() {
      this.mapText = "二叉搜索树";
      let treeData = this.checkTreeData(this.treeData);
      this.showTreeData = treeData;
      this.buildTree(this.showTreeData);
      this.treeTitle = "nomalTree";
      // 插入历史记录，没什么说的
      this.inputHistory.insert(treeData.join(" "));
    },
    searchDeeperLevel(tree) {
      // 最深的节点
      let deeperLevel = 0;
      // 先遍历找出最深的节点，设定root.x的值和每层增加的值y
      tree.preOrderTraverse((item) => {
        if (deeperLevel < item.level) {
          deeperLevel = item.level;
        }
      });
      return deeperLevel;
    },
    // 检查输入的值
    checkTreeData(treeData) {
      let res = [];
      // 检查输入的值，范围0~99且是数字
      let tempData = treeData.trim().replace(/\s+/g, " ").split(" ");
      for (let element of tempData) {
        element = parseInt(+element, 10);
        if (
          element < 0 ||
          element > 99 ||
          isNaN(element) ||
          res.indexOf(element) !== -1
        ) {
          this.$message({
            type: "warning",
            message: "请输入0~99且不重复的整数",
            duration: 2500,
          });
          return (res = []);
        }
        res.push(element);
      }
      return res;
    },
    // echarts绘制树
    tree() {
      let dom = document.getElementById("container");
      echarts.dispose(dom);
      let myChart = echarts.init(dom);
      // 随着窗口大小改变而重新渲染tree，太神奇了，没设定时器，比较耗能，但，我不在乎
      window.addEventListener("resize", function () {
        myChart.resize();
      });
      let app = {};
      let option;
      option = {
        // 调色盘
        color: this.mapColor,
        title: {
          text: this.mapText,
        },
        tooltip: {},
        animationDurationUpdate: 1000, //动画持续时间更新
        animationEasingUpdate: "quinticInOut", //动画缓和更新
        series: [
          {
            type: "graph",
            layout: "none",
            symbolSize: this.mapSymbolSize,
            roam: this.mapRoam, //放大缩小功能
            label: {
              show: true,
            },
            edgeSymbol: ["none", "none"],
            edgeSymbolSize: [4, 10],
            edgeLabel: {
              fontSize: 20,
            },
            data: this.mapData,
            // [{name: "11",x: 100,y: 0,}], data的属性格式
            links: this.mapLinks,
            // links:[{source: "11",target: "7",}],  links的属性格式
            lineStyle: {
              opacity: 0.9,
              width: 2,
              curveness: 0,
            },
          },
        ],
      };
      if (option && typeof option === "object") {
        myChart.setOption(option);
      }
    },
    destroyOldTree() {
      echartsInstance.dispose();
    },
  },
  watch: {
    mapRoam() {
      this.tree();
    },
    treeData() {
      window.sessionStorage.setItem("treeData", this.treeData);
    },
    alertStation() {
      window.sessionStorage.setItem("alertStation", this.alertStation);
    },
  },
};
</script>

<style lang="scss" scoped>
.home {
  #container {
    height: 800px;
    // width: 1500px;
    // width: 100%;
    border: 2px solid sandybrown;
  }
  .show-input {
    &:hover {
      cursor: pointer;
    }
  }
}
</style>
